package torrent

import (
	"errors"
	"io"
	"os"
	"path/filepath"
	"strings"

	"github.com/zeebo/blake3"
)

// ignoredNames 定义了需要忽略的特殊文件和目录名（全部小写）
var ignoredNames = map[string]struct{}{
	// Windows系统常见隐藏文件夹
	"$recycle.bin":              {},
	"system volume information": {},
	"thumbs.db":                 {},
	"desktop.ini":               {},
	"ehthumbs.db":               {},
	// macOS系统隐藏文件
	".ds_store":       {},
	".fseventsd":      {},
	".spotlight-v100": {},
	".trashes":        {},
	".apdisk":         {},
	// Linux系统常见隐藏文件夹及缓存
	".cache": {},
	".local": {},
	".gvfs":  {},
	".trash": {},
	// 其他常见缩略图缓存
	"iconcache.db":      {},
	"ehthumbs_vista.db": {},
}

// isIgnored 判断文件或目录名是否为忽略对象，忽略大小写
func (m *torrentManagerImpl) isIgnored(name string) bool {
	// 转换为小写进行比较，确保大小写不敏感
	name = strings.ToLower(name)
	_, ok := ignoredNames[name]
	return ok
}

// calculateBlockCount 计算文件的分块数量，不实际读取文件内容
func (m *torrentManagerImpl) calculateBlockCount(fileSize int64, pieceLength int) int {
	// 如果文件大小为0或块大小无效，返回0
	if fileSize == 0 || pieceLength <= 0 {
		return 0
	}
	// 计算需要多少个块来存储整个文件（向上取整）
	return int((fileSize + int64(pieceLength) - 1) / int64(pieceLength))
}

// hashFileWithProgress 带进度回调的文件哈希计算函数（按块上报进度）
func (m *torrentManagerImpl) hashFileWithProgress(path string, processedBlocks *int, progressCh chan<- float64, totalBlocks int, pieceLength int) (*FileInfo, []Hash, error) {
	// 打开文件进行读取
	f, err := os.Open(path)
	if err != nil {
		return nil, nil, err
	}
	defer f.Close()

	// 获取文件信息（主要是文件大小）
	info, err := f.Stat()
	if err != nil {
		return nil, nil, err
	}

	// 存储每个数据块的哈希值
	var blockHashes []Hash
	// 创建读取缓冲区，大小等于块大小
	buf := make([]byte, pieceLength)
	for {
		// 尝试读取一个完整的块
		n, err := io.ReadFull(f, buf)
		if err != nil && err != io.EOF && !errors.Is(err, io.ErrUnexpectedEOF) {
			return nil, nil, err
		}
		// 如果没有读取到任何数据，说明文件读取完毕
		if n == 0 {
			break
		}
		// 计算当前块的blake3哈希值
		h := blake3.Sum256(buf[:n])
		// 注意这里存储的是切片副本，避免引用问题
		blockHashes = append(blockHashes, h)

		// 更新已处理的块数并上报进度
		if processedBlocks != nil {
			*processedBlocks++
			if progressCh != nil && totalBlocks > 0 {
				// 计算当前进度百分比
				progress := float64(*processedBlocks) / float64(totalBlocks)
				if progress > 1.0 {
					progress = 1.0
				}
				// 非阻塞方式发送进度更新
				select {
				case progressCh <- progress:
				default:
				}
			}
		}

		// 如果遇到文件结束，跳出循环
		if err == io.EOF || errors.Is(err, io.ErrUnexpectedEOF) {
			break
		}
	}

	// 返回文件信息和块哈希列表
	return &FileInfo{
		Length: info.Size(),
	}, blockHashes, nil
}

// collectFilesAndTotalBlocks 收集所有文件路径并计算总分块数
func (m *torrentManagerImpl) collectFilesAndTotalBlocks(absRoot string, pieceLength int) ([]string, int, error) {
	var allFiles []string
	var totalBlocks int

	// 遍历目录树，收集所有文件
	err := filepath.Walk(absRoot, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 获取文件或目录的基本名称
		base := info.Name()
		// 检查是否为需要忽略的文件或目录
		if m.isIgnored(base) {
			if info.IsDir() {
				// 如果是目录，跳过整个目录
				return filepath.SkipDir
			}
			// 如果是文件，跳过当前文件
			return nil
		}

		// 只处理普通文件，不处理目录
		if !info.IsDir() {
			allFiles = append(allFiles, path)
			// 计算该文件的分块数量
			blockCount := m.calculateBlockCount(info.Size(), pieceLength)
			totalBlocks += blockCount
		}
		return nil
	})
	if err != nil {
		return nil, 0, err
	}
	return allFiles, totalBlocks, nil
}

// walkFilesForTorrentWithProgress 通过通道上报进度的文件扫描函数（按块级别）
func (m *torrentManagerImpl) walkFilesForTorrentWithProgress(absRoot string, pieceLength int, progressCh chan<- float64) ([]FileInfo, error) {
	var files []FileInfo

	// 第一步：收集所有文件路径并计算总块数
	allFiles, totalBlocks, err := m.collectFilesAndTotalBlocks(absRoot, pieceLength)
	if err != nil {
		return nil, err
	}

	// 第二步扫描：处理文件并基于分块数量计算进度
	processedBlocks := 0
	for _, path := range allFiles {
		// 获取文件信息
		info, err := os.Stat(path)
		if err != nil {
			return nil, err
		}

		// 再次检查是否为忽略文件（双重保险）
		base := info.Name()
		if m.isIgnored(base) {
			continue
		}

		// 计算相对于根目录的相对路径
		rel, err := filepath.Rel(absRoot, path)
		if err != nil {
			return nil, err
		}
		// 统一使用正斜杠作为路径分隔符
		rel = filepath.ToSlash(rel)

		// 使用带进度的哈希计算函数（在每个块后上报进度）
		fmeta, blockHashes, err := m.hashFileWithProgress(path, &processedBlocks, progressCh, totalBlocks, pieceLength)
		if err != nil {
			return nil, err
		}

		// 设置文件分块哈希
		fmeta.PieceHashes = blockHashes
		// 将相对路径拆分为路径片段数组
		fmeta.Path = strings.Split(rel, "/")
		files = append(files, *fmeta)

		// 额外一次进度更新（文件完成后），确保进度显示平滑
		if progressCh != nil && totalBlocks > 0 {
			progress := float64(processedBlocks) / float64(totalBlocks)
			if progress > 1.0 {
				progress = 1.0
			}
			// 非阻塞方式发送进度更新
			select {
			case progressCh <- progress:
			default:
			}
		}
	}
	return files, nil
}

// verifyFileOptimized 已移除：旧的逐块校验逻辑废弃，改为重建元数据进行对比
